﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Services;
using LucyGrid;
using System.Web.Script.Services;
using System.Diagnostics;

namespace WebAppTest
{
	/// <summary>
	/// Summary description for WebAppTest
	/// </summary>
	[WebService(Namespace = "http://tempuri.org/")]
	[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
	[System.ComponentModel.ToolboxItem(false)]
	// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
	// [System.Web.Script.Services.ScriptService]
	public class WebAppTest : System.Web.Services.WebService
	{
		[WebMethod]
		[ScriptMethod(UseHttpGet = true)]
		public string WebAppTestOperation()
		{
			return GridMe("Hello world " + DateTime.Now).ToString();
		}


		[WebMethod]
		[ScriptMethod(UseHttpGet = true)]
		public string[] OutputQueue()
		{
			return outq.Select(o => string.Format("{0}:{1}", o.Key, o.Value.data)).ToArray();
		}

		[WebMethod]
		[ScriptMethod(UseHttpGet = true)]
		public string[] OutputDump()
		{
			return chunkInfoDump.ToArray();
		}

		GridObject GridMe(string data) {
			GridObject o = new GridObject()
			{
				id = NextId(),
				data = "Hello world ",
				processStart = DateTime.Now
			};
			if (o == null)
				throw new Exception("Error creating GridObject");
			inq.Enqueue(o);
			while(true){
				gridEvent.WaitOne(1000);
				if(outq.ContainsKey(o.id)){
					if(outq.TryRemove(o.id, out o))
						return o;
				}
			}
		}

		
		[Serializable]
		class GridObject {
			public ulong id;
			public int processId;
			public string data;
			public DateTime processStart;
			public DateTime processGrid;
			public DateTime processEnd;

			public override string ToString()
			{
				return string.Format("PID: {5}, TOTAL: {0} ms. S: {1} G:{2} E:{3} DATA:{4}", 
					(processEnd - processStart).TotalMilliseconds 
					,processStart.ToString("hh:mm:ss")
					, processGrid.ToString("hh:mm:ss")
					, processEnd.ToString("hh:mm:ss")
					, data
					,processId);
			}
		}
		static ulong traceId = 0;
		static Object lockO = new object();
		static ConcurrentQueue<GridObject> inq;
		static ConcurrentDictionary<ulong, GridObject> outq;
		static AutoResetEvent gridEvent;
		static List<string> chunkInfoDump = new List<string>();
		static WebAppTest() {
			GridUtils.LogCustomOutput = (tags, msg) =>
			{
				if (tags.Contains("DUMP"))
					chunkInfoDump.Add(msg);
			};
			gridEvent = new AutoResetEvent(false);
			inq = new ConcurrentQueue<GridObject>();
			outq = new ConcurrentDictionary<ulong, GridObject>();
			Task.Run(() => Worker());
		}

		static ulong NextId() {
			lock (lockO)
			{
				return traceId++;
			}
		}
		static IEnumerable<GridObject> Generator() {
			GridObject outo;
			while (true) {
				if (inq.TryDequeue(out outo))
					yield return outo;
				else
					Thread.Sleep(200);
			}
		}
		static void Worker() {
			foreach (var o in Generator().AsParallelGrid(o => { 
				o.processGrid = DateTime.Now;
				o.processId = (int)Process.GetCurrentProcess().Id;
				return o; 
			}, new GridSettings() { EnableLocalProcessing = false }))
			{
				o.processEnd = DateTime.Now;
				outq.TryAdd(o.id, o);
				gridEvent.Set();
				// System.Diagnostics.Debug.WriteLine("Output {0}", o.id);
			}
		}
	}
}
